#include "individual.hh"
#include "constants.hh"
#include <iostream>
using namespace std;
using namespace evolution;

Individual::~Individual() { }

float
Individual::genetic_distance (Individual const* other) const {
    const pair<int, int> my_range = genome->get_innovation_range();
    const pair<int, int> other_range = other->genome->get_innovation_range();

    const int min = std::min(my_range.first, other_range.first);
    const int max = std::max(my_range.second, other_range.second);

    std::shared_ptr<Genome> const& a = genome;
    std::shared_ptr<Genome> const& b = other->genome;
 
    int excess = 0;
    int disjoint = 0;
    int common = 0;
    float delta_weights = 0;

    for (int i = min; i < max+1; ++i) {
        if (a->is_in_range(i) && b->is_in_range(i)) {
            bool a_has_gene = a->has_gene(i), b_has_gene = b->has_gene(i);
            if (a_has_gene || b_has_gene) {
                if (!(a_has_gene && b_has_gene)) {
                    disjoint++;
                } else {
                    common++;
                    delta_weights += abs(a->get_gene(i)->get_weight() - b->get_gene(i)->get_weight());
                }
            }

        } else {
            if (a->is_in_range(i) && a->has_gene(i)) excess++;
            else if (b->is_in_range(i) && b->has_gene(i)) excess++;
        }
    }

    
    if (common != 0) {
        delta_weights /= common;
    }

    const int N = std::max(a->get_connection_genes().size(), b->get_connection_genes().size());

    return excess*EXCESS_DISTANCE_FACTOR/N 
         + disjoint*DISJOINT_DISTANCE_FACTOR/N
         + delta_weights*WEIGHT_DISTANCE_FACTOR;
}
